home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 January: Mac OS SDK / Dev.CD Jan 99 SDK1.toast / Development Kits / Mac OS USB DDK_v1.0.1 / Examples / PrinterClassDriver / TradDriverLoaderLib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-09-03  |  22.4 KB  |  792 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        TradDriverLoaderLib.c
  3.  
  4.     Contains:    Implementation for the pseudo-DriverLoaderLib for 'DRVR's.
  5.  
  6.     Version:    xxx put version here xxx
  7.  
  8.     Written by:    Quinn "The Eskimo!"
  9.  
  10.     Copyright:    © 1996-1998 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     File Ownership:
  13.  
  14.         DRI:                Craig Keithley
  15.  
  16.         Other Contact:        xxx put other contact here xxx
  17.  
  18.         Technology:            USB Drivers
  19.  
  20.     Writers:
  21.  
  22.         (CJK)    Craig Keithley
  23.  
  24.     Change History (most recent first):
  25.  
  26.       <USB2>     5/28/98    CJK        change file creater to 'MPS '
  27.     You may incorporate this sample code into your applications without
  28.     restriction, though the sample code has been provided "AS IS" and the
  29.     responsibility for its operation is 100% yours.  However, what you are
  30.     not permitted to do is to redistribute the source as "DSC Sample Code"
  31.     after having made changes. If you're going to re-distribute the source,
  32.     we require that you make it clear in the source that the code was
  33.     descended from Apple Sample Code, but that you've made changes.
  34. */
  35.  
  36. #include <LowMem.h>
  37. #include <DriverGestalt.h>
  38. #include <TextUtils.h>
  39.  
  40. // Switched from using:
  41. //
  42. //   #include <PLStringFuncs.h>
  43. //
  44. // to using BlockMoveData because it's so hard to get PLstrcpy working
  45. // across a zillion different compilers.  *sigh*
  46.  
  47. #include "TradDriverLoaderLib.h"
  48.  
  49. ///////////////////////////////////////////////////////////////////////////
  50.  
  51. extern pascal SInt16 TradHigherDriverVersion(NumVersion *dv1, NumVersion *dv2)
  52. {
  53.     UInt16 nonRelRev1, nonRelRev2;
  54.  
  55.     if (dv1->majorRev           > dv2->majorRev)        return  1;
  56.     if (dv1->majorRev           < dv2->majorRev)        return -1;
  57.     if (dv1->minorAndBugRev     > dv2->minorAndBugRev)  return  1;
  58.     if (dv1->minorAndBugRev     < dv2->minorAndBugRev)  return -1;
  59.     if (dv1->stage              > dv2->stage)           return  1;
  60.     if (dv1->stage              < dv2->stage)           return -1;
  61.  
  62.     nonRelRev1 = dv1->nonRelRev;
  63.     nonRelRev2 = dv2->nonRelRev;
  64.     
  65.     if (dv1->stage == finalStage) {
  66.         if (dv1->nonRelRev == 0)                 nonRelRev1 = 0xFFFF;
  67.         if (dv2->nonRelRev == 0)                 nonRelRev2 = 0xFFFF;
  68.     }
  69.  
  70.     if (nonRelRev1 > nonRelRev2)                        return  1;
  71.     if (nonRelRev1 < nonRelRev2)                        return -1;
  72.  
  73.     return 0;
  74. }
  75.  
  76.  
  77. ///////////////////////////////////////////////////////////////////////////
  78.  
  79. extern pascal UnitNumber TradHighestUnitNumber(void)
  80.     // See comment in header file.
  81. {
  82.     return ( LMGetUnitTableEntryCount() - 1);
  83. }
  84.  
  85. ///////////////////////////////////////////////////////////////////////////
  86.  
  87. extern pascal Boolean TradDriverGestaltIsOn(DriverFlags flags)
  88.     // See comment in header file.
  89. {
  90.     return ( (flags & kmDriverGestaltEnableMask) != 0 );
  91. }
  92.  
  93. ///////////////////////////////////////////////////////////////////////////
  94.  
  95. static OSErr DriverGestaltOnOff(DriverRefNum refNum, Boolean setIt)
  96.     // This routine is called by TradDriverGestaltOn and
  97.     //  TradDriverGestaltOff to either set or clear the
  98.     //  kmDriverGestaltEnableMask bit in the DCE flags.
  99. {
  100.     OSErr err;
  101.     AuxDCEHandle thisDCE;
  102.     
  103.     // First called TradGetDriverInformation to validate the refNum
  104.     //  and verify that the driver exists.
  105.     err = TradGetDriverInformation(refNum, nil, nil, nil, nil);
  106.     if (err == noErr) {
  107.         thisDCE = (AuxDCEHandle) GetDCtlEntry(refNum);
  108.         if (setIt) {
  109.             (**thisDCE).dCtlFlags |= kmDriverGestaltEnableMask;
  110.         } else {
  111.             (**thisDCE).dCtlFlags &= ~kmDriverGestaltEnableMask;
  112.         }
  113.     }
  114.     
  115.     return (err);
  116. }
  117.  
  118. ///////////////////////////////////////////////////////////////////////////
  119.  
  120. extern pascal OSErr TradDriverGestaltOn(DriverRefNum refNum)
  121.     // See comment in header file.
  122. {
  123.     return ( DriverGestaltOnOff(refNum, true) );
  124. }
  125.  
  126. ///////////////////////////////////////////////////////////////////////////
  127.  
  128. extern pascal OSErr TradDriverGestaltOff(DriverRefNum refNum)
  129.     // See comment in header file.
  130. {
  131.     return ( DriverGestaltOnOff(refNum, false) );
  132. }
  133.  
  134. ///////////////////////////////////////////////////////////////////////////
  135.  
  136. extern pascal OSErr TradOpenInstalledDriver(DriverRefNum refNum, SInt8 ioPermission)
  137.     // See comment in header file.
  138. {
  139.     OSErr                 err;
  140.     Str255                driverName;
  141.     DriverRefNum    realRefNum;
  142.  
  143.     // Check parameters.
  144.     err = noErr;
  145.     if (ioPermission != fsRdWrPerm) {
  146.         err = paramErr;
  147.     }
  148.     
  149.     // Get the name of the driver, then simply open it.
  150.     if (err == noErr) {
  151.         err = TradGetDriverInformation(refNum, nil, nil, driverName, nil);
  152.     }
  153.     if (err == noErr) {
  154.         if ( driverName[0] == 0 ) {
  155.             err = paramErr;
  156.         }
  157.     }
  158.     if (err == noErr) {
  159.         err = OpenDriver(driverName, &realRefNum);
  160.     }
  161.     if (err == noErr) {
  162.         if (realRefNum != refNum) {
  163.             err = paramErr;        // My favourite error code -- at some intrinsic level, every error is a paramErr (-;
  164.         }
  165.     }
  166.     
  167.     return (err);
  168. }
  169.  
  170. ///////////////////////////////////////////////////////////////////////////
  171.  
  172. extern pascal OSErr TradLookupDrivers(UnitNumber beginningUnit,
  173.                                         UnitNumber endingUnit,
  174.                                         Boolean emptyUnits,
  175.                                         ItemCount *returnedRefNums, 
  176.                                         DriverRefNum *refNums)
  177.     // See comment in header file.
  178. {
  179.     OSErr err;
  180.     AuxDCEHandle     *unitTable;
  181.     ItemCount         maxRefNums;
  182.     UnitNumber         currentUnit;
  183.     
  184.     // Sanity check the parameters.
  185.     if ( endingUnit > TradHighestUnitNumber() ) {
  186.         endingUnit = TradHighestUnitNumber();
  187.     }
  188.     err = noErr;
  189.     if ( beginningUnit > TradHighestUnitNumber() ) {
  190.         err = badUnitErr;
  191.     }
  192.     if (err == noErr) {
  193.         if (beginningUnit > endingUnit ) {
  194.             err = paramErr;
  195.         }
  196.     }
  197.  
  198.     // Now do the real work...
  199.     if (err == noErr) {
  200.         unitTable = (AuxDCEHandle *) LMGetUTableBase();
  201.  
  202.         maxRefNums = *returnedRefNums;
  203.         
  204.         // Loop through each unit table entry from beginningUnit to endingUnit inclusive.
  205.         *returnedRefNums = 0;
  206.         currentUnit = beginningUnit;
  207.         while ( currentUnit <= endingUnit ) {
  208.  
  209.             // If we've still got space to return a unit...
  210.             if ( *returnedRefNums < maxRefNums ) {
  211.             
  212.                 // and we're interested in this unit...
  213.                 if (    (emptyUnits && unitTable[currentUnit] == nil) ||
  214.                             (!emptyUnits && unitTable[currentUnit] != nil) ) {
  215.                     
  216.                     // then copy the unit out to the caller's array
  217.                     refNums[*returnedRefNums] = ~currentUnit;
  218.                     *returnedRefNums += 1;
  219.                 }
  220.             }
  221.             currentUnit += 1;
  222.         }
  223.     
  224.     }
  225.     
  226.     return (err);
  227. }
  228.  
  229. ///////////////////////////////////////////////////////////////////////////
  230.  
  231. enum {
  232.     kNoUnitNumber = 0xFFFF
  233. };
  234.  
  235. static UnitNumber IsDriverInstalled(ConstStr255Param name, UnitNumber skipThisUnit)
  236.     // Look through the unit table to see if there is a driver with this name
  237.     //  already installed.  Note that you might consider calling OpenDriver
  238.     //  here, but that would be wrong.  OpenDriver has similar semantics, but
  239.     //  if it fails to find a driver in the unit table it will search the
  240.     //  current resource chain looking for a DRVR resource to install.
  241.     //  Given that it's likely our client has a DRVR resource in their
  242.     //  resource chain ('cause they're messing around trying to install
  243.     //  drivers), and that OpenDriver will install it without detaching
  244.     //  it from the client's resource file, and that the client's
  245.     //  resource file may go away (ie they're a DropMounter-like application
  246.     //  or some INIT running at system startup), this would be bad.
  247. {
  248.     UnitNumber    endingUnit;
  249.     UnitNumber    unit;
  250.     Str255            unitName;
  251.     
  252.     endingUnit = TradHighestUnitNumber();
  253.     
  254.     for (unit = 0; unit <= endingUnit; unit++) {
  255.         if ( TradGetDriverInformation(~unit, nil, nil, unitName, nil) == noErr) {
  256.             if ( unit != skipThisUnit && EqualString(name, unitName, false, true) ) {
  257.                 return (unit);
  258.             }
  259.         }
  260.     }
  261.     
  262.     return (kNoUnitNumber);
  263. }
  264.  
  265. ///////////////////////////////////////////////////////////////////////////
  266.  
  267. enum {
  268.     kMaximumNumberOfUnitTableEntries = 1024,
  269.     // kMaximumNumberOfUnitTableEntries = 8000,
  270.     
  271.     // kMaximumNumberOfUnitTableEntries is documented in Technote
  272.     //  DV 23 "Driver Education" <http://devworld.apple.com/dev/technotes/dv/dv_23.html>
  273.     //  as being the maximum size that the classic Device Manager
  274.     //  would grow the unit table.  In theory, this limits the system
  275.     //  to 128 unit table entries.
  276.     // However the traditional Mac OS is capable of dealing with much more
  277.     //  than 128 units.  In fact, some multi-port serial card vendors
  278.     //  regularly install more.
  279.     // Prior to Mac OS 8, the PCI DriverLoaderLib enforced the 128 limit.
  280.     //  I filed a bug against the PCI DriverLoaderLib to get this limit
  281.     //  raised, and the new limit under Mac OS 8 is 1024.
  282.     // Given that official sanction, I have now raised the standard
  283.     //  limit enforced by this library to 1024.  I supply an alternative
  284.     //  maximum (8000), designed to keep the unit table smaller than 32K.
  285.     //  This is important because many people use 68K word indexing
  286.     //  (ie x(a0,d0.w) to to access the entries.
  287.     // I have tested TradDriverLoaderLib installing up to 500 device
  288.     //  drivers.  
  289.     
  290.     kNumberOfEntriesToGrowUnitTable = 4
  291.     
  292.     // Technote DV 23 "Driver Education"
  293.     //  <http://devworld.apple.com/dev/technotes/dv/dv_23.html>
  294.     //  documents that the system grows the unit table by 4 entries
  295.     //  at a time.
  296. };
  297.  
  298. ///////////////////////////////////////////////////////////////////////////
  299.  
  300. static OSErr GrowUnitTable()
  301.     // This routine grows the unit table by kNumberOfEntriesToGrowUnitTable,
  302.     //  up to a maximum of kMaximumNumberOfUnitTableEntries.  The routine
  303.     //  is guaranteed to grow the table by at least one entry, or fail
  304.     //  with an error.
  305. {
  306.     OSErr        err;
  307.     Ptr         oldTable;
  308.     Ptr         newTable;
  309.     UInt32    oldCount;
  310.     UInt32    newCount;
  311.  
  312.     // Get the info about the old table, and calculate the new table size.    
  313.     oldTable = LMGetUTableBase();
  314.     oldCount = LMGetUnitTableEntryCount();
  315.     newCount = oldCount + kNumberOfEntriesToGrowUnitTable;
  316.  
  317.     // Guard against growing the table too big.    
  318.     err = noErr;
  319.     if (newCount > kMaximumNumberOfUnitTableEntries) {
  320.         err = unitTblFullErr;
  321.     }
  322.     
  323.     // Allocate the new unit table in the system heap.  Note that we
  324.     //  clear the newly allocated memory, so that later on, when we
  325.     //  use this memory as the new unit table, the newly allocated
  326.     //  entries will be empty.
  327.  
  328.     if (err == noErr) {
  329.         newTable = NewPtrSysClear( newCount * sizeof(AuxDCEHandle));
  330.         err = MemError();
  331.     }
  332.  
  333.     // Copy the unit table entries over to the new table and then switch
  334.     //  to that table.  Note that this sequence doesn't disable interrupts,
  335.     //  instead it relies on the fact that programs can't modify the
  336.     //  unit table at interrupt time, and thus we, running at non-interrupt
  337.     //  time, have exclusive write access to the table.
  338.  
  339.     // Note that the sequence of these next few lines is *very* important.
  340.     //  If we did this in any other order, you could get to a situation
  341.     //  where interrupt code might be looking at an inconsistent 
  342.     //  unit table, which would be bad.
  343.     
  344.     // The sequence is:
  345.     //  1. copy the old unit table entries to the new table
  346.     //  2. change the unit table base pointer, so that interrupts
  347.     //         start using the new unit table
  348.     //  3. then change the unit table count, so that we have
  349.     //         more entries available
  350.     
  351.     if (err == noErr) {
  352.         BlockMoveData(oldTable, newTable, oldCount * sizeof(AuxDCEHandle));    // 1.
  353.         LMSetUTableBase(newTable);                                                                                    // 2.
  354.         LMSetUnitTableEntryCount(newCount);                                                                    // 3.
  355.         
  356.         // Now its safe to dispose of the old unit table.
  357.         DisposePtr(oldTable);
  358.     }
  359.  
  360.     return (err);    
  361. }
  362.  
  363. ///////////////////////////////////////////////////////////////////////////
  364.  
  365. static OSErr FindFreeUnitNumber(UnitNumber beginningUnit,
  366.                                 UnitNumber endingUnit, 
  367.                                 UnitNumber *foundUnit)
  368.     // This routine walks the unit table looking for a free
  369.     //  slot.  The slot must be between beginningUnit
  370.     //  and endingUnit.  If endingUnit is greater than
  371.     //  TradHighestUnitNumber(), then we're allowed
  372.     //  to grow the unit table to meet our needs.
  373. {
  374.     OSErr err;
  375.     Boolean found;
  376.     UnitNumber currentUnit;
  377.     UnitNumber trueEndingUnit;
  378.     AuxDCEHandle *unitTable;
  379.     
  380.     unitTable = (AuxDCEHandle *) LMGetUTableBase();
  381.     
  382.     // Find trueEndingUnit, which is the minimum
  383.     //  of endingUnit and the highest unit number.
  384.     trueEndingUnit = endingUnit;
  385.     if ( trueEndingUnit > TradHighestUnitNumber() ) {
  386.         trueEndingUnit = TradHighestUnitNumber();
  387.     }
  388.  
  389.     // Scan through the unit table, starting at beginningUnit
  390.     //  and ending at trueEndingUnit, looking for an
  391.     //  empty slot.
  392.     currentUnit = beginningUnit;
  393.     found = false;
  394.     while (currentUnit <= trueEndingUnit && !found) {
  395.         found = (unitTable[currentUnit] == nil);
  396.         if (!found) {
  397.             currentUnit += 1;
  398.         }
  399.     }
  400.  
  401.     // Finish up.    
  402.     if (found) {
  403.         // We found an empty slot, return it.
  404.         *foundUnit = currentUnit;
  405.         err = noErr;
  406.     } else {
  407.  
  408.         // We didn't find an empty slot.  If we're
  409.         //  allowed to, grow the unit table, otherwise
  410.         //  just return an error.
  411.         
  412.         if (endingUnit > trueEndingUnit) {
  413.             err = GrowUnitTable();
  414.             if (err == noErr) {
  415.                 *foundUnit = trueEndingUnit + 1;
  416.             }
  417.         } else {
  418.             err = unitTblFullErr;
  419.         }
  420.     }
  421.     
  422.     return (err);    
  423. }
  424.  
  425. ///////////////////////////////////////////////////////////////////////////
  426.  
  427. extern pascal OSErr TradInstallDriverFromPtr(DRVRHeaderPtr driver,
  428.                                                 UnitNumber beginningUnit,
  429.                                                 UnitNumber endingUnit,
  430.                                                 DriverRefNum *refNum)
  431.     // See comment in header file.
  432. {
  433.     OSErr err;
  434.     UnitNumber foundUnit;
  435.     AuxDCEHandle theDCE;
  436.     
  437.     // Sanity check parameters.
  438.     err = noErr;
  439.     if ( driver == nil ) {
  440.         err = paramErr;
  441.     }
  442.     if ( beginningUnit > TradHighestUnitNumber() ) {
  443.         err = badUnitErr;
  444.     }
  445.     if ( err == noErr && beginningUnit > endingUnit ) {
  446.         err = paramErr;
  447.     }
  448.     
  449.     // Check whether this driver is already installed.
  450.     if ( err == noErr ) {
  451.         // Check whether it's already installed.
  452.         foundUnit = IsDriverInstalled(&driver->drvrName[0], kNoUnitNumber);
  453.         if (foundUnit != kNoUnitNumber) {
  454.             // Return the refnum of the existing driver to the caller.
  455.             *refNum = ~foundUnit;
  456.             err = dupFNErr;
  457.         }
  458.     }
  459.     
  460.     // Now walk the unit table looking for a free slot.
  461.     if (err == noErr) {
  462.         err = FindFreeUnitNumber(beginningUnit, endingUnit, &foundUnit);
  463.     }
  464.  
  465.     // We've got a free slot, so let's install the device driver.
  466.     //  Note that we use DriverInstallReserveMem, rather than the standard
  467.     //  DriverInstall, so that the DCE is allocated low in the system
  468.     //  heap.  DriverInstallReserveMem was introduced with the 128K ROM.
  469.  
  470.     if (err == noErr) {
  471.         err = DriverInstallReserveMem(driver, ~foundUnit);
  472.     }
  473.     
  474.     // Now do some important tidying up.
  475.     if (err == noErr) {
  476.  
  477.         // Return the refNum to the caller.
  478.         *refNum = ~foundUnit;
  479.  
  480.         theDCE = (AuxDCEHandle) GetDCtlEntry(*refNum);
  481.         
  482.         // Now setup the DCE properly.  There's a whole pile of things we
  483.         //  have to do, mainly because DriverInstall is such a brain-dead
  484.         //  routine.
  485.         
  486.         // First up, DriverInstall seems to ignore the first parameter
  487.         //  passed to it, so we have to blat the pointer to the driver code in
  488.         //  yourself afterwards.
  489.         
  490.         (**theDCE).dCtlDriver = (Ptr) driver;
  491.  
  492.         // Then we have to set up the flags.  We do this by copying the flags
  493.         //  out of the first word of the driver code.  We make sure to clear
  494.         //  the dRAMBased bit because we're actually a pointer-based driver
  495.         //  and DriverInstallReserveMem sets it to provisionally indicate that
  496.         //  we're a handle based driver.  We also set dNeedLock because
  497.         //  we want the the Device Manager to lock down the DCE.
  498.         
  499.         (**theDCE).dCtlFlags = (driver->drvrFlags & ~dRAMBasedMask) | dNeedLockMask;
  500.  
  501.         // There's also a bunch of fields we copy straight across without
  502.         //  any modification.  You might expect DriverInstall to copy
  503.         //  across these fields from the driver header to the DCE, but it doesn't
  504.         //  do that, so we do it ourselves.
  505.  
  506.         (**theDCE).dCtlDelay = driver->drvrDelay;
  507.         (**theDCE).dCtlEMask = driver->drvrEMask;
  508.         (**theDCE).dCtlMenu  = driver->drvrMenu;
  509.  
  510.         // Finally, we lock the DCE.
  511.         // Note that strictly speaking we don't need to HLock the DCE
  512.         //  because the Device Manager will do it when it you open a driver
  513.         //  that has dNeedLock set.  However, we want to
  514.         //  lock it now because DriverInstallReserveMem has just made sure
  515.         //  that the DCE was created low in the system heap, so we might as
  516.         //  well lock it down low rather than let it float.
  517.  
  518.         HLock( (Handle) theDCE );
  519.     }
  520.     
  521.     return (err);
  522. }
  523.  
  524. ///////////////////////////////////////////////////////////////////////////
  525.  
  526. extern pascal OSErr TradInstallDriverFromHandle(DRVRHeaderHandle driver,
  527.                                                 UnitNumber beginningUnit,
  528.                                                 UnitNumber endingUnit,
  529.                                                 DriverRefNum *refNum)
  530.     // See comment in header file.
  531. {
  532.     OSErr err;
  533.     Size  driverSize;
  534.     DRVRHeaderPtr driverPtr;
  535.     
  536.     driverPtr = nil;
  537.     
  538.     err = noErr;
  539.     if (driver == nil || *driver == nil) {
  540.         err = paramErr;
  541.     }
  542.     if (err == noErr) {
  543.         driverSize = GetHandleSize( (Handle) driver );
  544.     }
  545.     if (err == noErr) {
  546.         driverPtr = (DRVRHeaderPtr) NewPtrSys( driverSize );
  547.         err = MemError();
  548.     }
  549.     
  550.     if (err == noErr) {
  551.         // This is *not* a BlockMoveData call. This time, we really are moving code!
  552.         //  I could have put cache flushing code in here, but then I would have
  553.         //  had to check whether it was available or not.
  554.         BlockMove( *driver, driverPtr, driverSize );
  555.         
  556.         err = TradInstallDriverFromPtr(driverPtr, beginningUnit, endingUnit, refNum);
  557.     }
  558.     
  559.     // Clean up.
  560.     if (err != noErr) {
  561.         // We're returning an error.  The API says we should leave the handle untouched,
  562.         //  but we should definitely clean up our new copy of the driver code.
  563.         if (driverPtr != nil) {
  564.             DisposePtr( (Ptr) driverPtr );
  565.         }
  566.     }
  567.     
  568.     return (err);
  569. }
  570.  
  571. ///////////////////////////////////////////////////////////////////////////
  572.  
  573. extern pascal OSErr TradInstallDriverFromResource(SInt16 rsrcID, StringPtr rsrcName,
  574.                                                 UnitNumber beginningUnit,
  575.                                                 UnitNumber endingUnit,
  576.                                                 DriverRefNum *refNum)
  577.     // See comment in header file.
  578. {
  579.     OSStatus err;
  580.     Handle driverHandle;
  581.     
  582.     // Note: We don't care which zone the resource gets loaded, because 
  583.     //  TradInstallDriverFromHandle makes a copy of it anyway.
  584.  
  585.     // Get the resource, using either rsrcID or rsrcName.
  586.     if (rsrcName == nil) {
  587.         driverHandle = Get1Resource('DRVR', rsrcID);
  588.     } else {
  589.         driverHandle = Get1NamedResource('DRVR', rsrcName);
  590.     }
  591.     
  592.     // Set err if we couldn't get the resource.
  593.     if (driverHandle == nil) {
  594.         err = ResError();
  595.         if (err == noErr) {
  596.             err = resNotFound;
  597.         }
  598.     } else {
  599.         // Make sure we're not killed by some clown making the 'DRVR' purgeable.
  600.         HNoPurge(driverHandle);                    
  601.         err = MemError();
  602.     }
  603.     
  604.     // Now install the driver as if we'd got it from a memory handle.    
  605.     if (err == noErr) {
  606.         err = TradInstallDriverFromHandle( (DRVRHeaderHandle) driverHandle, beginningUnit, endingUnit, refNum);
  607.  
  608.         ReleaseResource(driverHandle);
  609.         if (err == noErr) {
  610.             err = ResError();
  611.         }
  612.     }
  613.     
  614.     return (err);
  615. }
  616.  
  617. ///////////////////////////////////////////////////////////////////////////
  618.  
  619. extern pascal OSErr TradGetDriverInformation(DriverRefNum refNum,
  620.                                                 UnitNumber *thisUnit,
  621.                                                 DriverFlags *flags,
  622.                                                 StringPtr name,
  623.                                                 DRVRHeaderPtr *driverHeader
  624.                                                 )
  625.     // See comment in header file.
  626. {
  627.     OSErr err;
  628.     UnitNumber             tmpUnit;
  629.     AuxDCEHandle        tmpDCE;
  630.     DRVRHeaderPtr        tmpHeader;
  631.     DRVRHeaderHandle    tmpDriverHandle;
  632.     
  633.     // Get some initial information.
  634.     tmpUnit = ~refNum;
  635.     
  636.     // Sanity check the refNum parameter.
  637.     err = noErr;
  638.     if (tmpUnit > TradHighestUnitNumber()) {
  639.         err = badUnitErr;
  640.     }
  641.     if (err == noErr) {
  642.         tmpDCE = (AuxDCEHandle) GetDCtlEntry(refNum);
  643.         if ( tmpDCE == nil ) {
  644.             err = unitEmptyErr;
  645.         }
  646.     }
  647.     if (err == noErr) {
  648.         if ( (*tmpDCE == nil) || (GetHandleSize( (Handle) tmpDCE) < sizeof(DCtlEntry)) ) {
  649.             err = dceExtErr;
  650.         }
  651.     }
  652.     
  653.     // Get the information from the DCE.
  654.     if (err == noErr) {
  655.  
  656.         // From the DCE, find the DRVR header.  This can fail for a number of reasons:
  657.         //     1. dCtlDriver is nil
  658.         //     2. the driver is handle based, and the handle's master point is nil
  659.         //     3. the driver is handle based, and the driver's handle is too small
  660.         // In all of these cases, we set tmpHeader to nil, returning nil to our
  661.         // client.
  662.         
  663.         tmpHeader = (DRVRHeaderPtr) (**tmpDCE).dCtlDriver;
  664.         if ( tmpHeader != nil ) {
  665.             if ( ((**tmpDCE).dCtlFlags & dRAMBasedMask) != 0 ) {
  666.  
  667.                 tmpDriverHandle = (DRVRHeaderHandle) tmpHeader;
  668.                 
  669.                 if ( (*tmpDriverHandle != nil) &&
  670.                             (GetHandleSize( (Handle) tmpDriverHandle) >= sizeof(DRVRHeader)) ) {
  671.                     tmpHeader = *tmpDriverHandle;
  672.                 }
  673.             }
  674.         }
  675.         
  676.         // Now copy out the various requested parameters
  677.         if (thisUnit != nil) {
  678.             *thisUnit = tmpUnit;
  679.         }
  680.         if (flags != nil) {
  681.             *flags = (**tmpDCE).dCtlFlags;
  682.         }
  683.         if (name != nil) {
  684.             if ( tmpHeader == nil ) {
  685.                 name[0] = 0;
  686.             } else {
  687.                 BlockMoveData(&tmpHeader->drvrName[0], name, tmpHeader->drvrName[0] + 1);
  688.             }
  689.         }
  690.         if (driverHeader != nil) {
  691.             *driverHeader = tmpHeader;
  692.         }
  693.     }
  694.     
  695.     return (err);
  696. }
  697.  
  698. ///////////////////////////////////////////////////////////////////////////
  699.  
  700. extern pascal OSErr TradRemoveDriver(DriverRefNum refNum, Boolean immediate)
  701.     // See comment in header file.
  702. {
  703.     OSErr                 err;
  704.     DriverFlags     flags;
  705.     DRVRHeaderPtr driverHeader;
  706.  
  707.     // Check parameters.
  708.     err = noErr;
  709.     if (immediate) {
  710.         err = paramErr;
  711.     }
  712.  
  713.     // Get information about the driver we're closing.
  714.     if (err == noErr) {
  715.         err = TradGetDriverInformation(refNum, nil, &flags, nil, &driverHeader);
  716.     }
  717.     if (err == noErr) {
  718.         if ( driverHeader == nil ) {
  719.             err = paramErr;
  720.         }
  721.     }
  722.     
  723.     // If the driver is open, close it.
  724.     if (err == noErr) {
  725.         if ( (flags & dOpenedMask) != 0 ) {
  726.             err = CloseDriver(refNum);
  727.         }
  728.     }
  729.     
  730.     // Now call the system to remove the driver from the unit table.  Note that this
  731.     //  works because of a subtlety in DriverRemove.  If the driver being removed
  732.     //  is a RAM-based driver (which our drivers aren't), DriverRemove will call
  733.     //  ReleaseResource on the dCtlDriver.  We don't want this, so we make our drivers
  734.     //  not RAM-based.
  735.     
  736.     if (err == noErr) {
  737.         err = DriverRemove(refNum);
  738.     }
  739.     
  740.     if (err == noErr) {
  741.         // All is cool, so let's dispose of the code.
  742.         DisposePtr( (Ptr) driverHeader);
  743.     }
  744.     
  745.     return (err);
  746. }
  747.  
  748. ///////////////////////////////////////////////////////////////////////////
  749.  
  750. extern pascal OSErr TradRenameDriver(DriverRefNum refNum, ConstStr255Param newDriverName)
  751.     // See *important* comment in header file.
  752. {
  753.     OSErr             err;
  754.     Str255             driverName;
  755.     DRVRHeaderPtr     driverHeader;
  756.     
  757.     err = noErr;
  758.     if ( newDriverName[0] == 0 ) {
  759.         err = paramErr;
  760.     }
  761.     if (err == noErr) {
  762.         // Get information about the driver we're renaming.
  763.         err = TradGetDriverInformation(refNum, nil, nil, driverName, &driverHeader);
  764.     }
  765.     if (err == noErr) {
  766.         if ( driverHeader == nil ) {
  767.             err = paramErr;
  768.         }
  769.     }
  770.     
  771.     // Now check the name lengths.  See comment in implementation for details.
  772.     if (err == noErr) {
  773.         if ( newDriverName[0] > driverName[0] ) {
  774.             err = paramErr;
  775.         }
  776.     }
  777.     
  778.     // Now check whether the new name is already present in the unit table.
  779.     if (err == noErr) {
  780.         if ( IsDriverInstalled(newDriverName, ~refNum) != kNoUnitNumber ) {
  781.             err = dupFNErr;
  782.         }
  783.     }
  784.     
  785.     // Now copy in the new driver name.
  786.     if (err == noErr) {
  787.         BlockMoveData( newDriverName, &driverHeader->drvrName[0], newDriverName[0] + 1 );
  788.     }
  789.     
  790.     return (err);
  791. }
  792.